iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 11
0
Modern Web

從技術文章深入學習 JavaScript系列 第 22

Day 22 [編程01] JavaScript 複雜判斷的更優雅寫法

  • 分享至 

  • xImage
  •  

作者:Think.

連接:https://juejin.im/post/6844903705058213896

來源:掘金

本文主要解決甚麼

我們時常寫一些判斷,有時候條件過多會讓代碼看起來很臃腫且不直觀,本文作者整理了一些更優雅的寫法。

一元邏輯判斷例子

輸入分數,依照分數區段輸出等第以及訊息

if else寫法

const getScoreInfo = (score) => {
  let level = Math.floor(score / 10)
  if (level == 10) {
    sendLevel('等第: A')
    sendMessage('好強!')
  } else if (level == 9) {
    sendLevel('等第: B')
    sendMessage('繼續保持')
  } else if (level == 8) {
    sendLevel('等第: B')
    sendMessage('繼續保持!')
  } else if (level == 7) {
    sendLevel('等第: C')
    sendMessage('還有進步空間')
  } else if (level == 6) {
    sendLevel('等第: D')
    sendMessage('要再加強')
  } else {
    sendLevel('等第: F')
    sendMessage('加油')
  }
}

switch寫法

看起來節省很多,而且80分以上跟90分以上輸出一樣,可以用break

const getScoreInfo = (score) => {
  let level = Math.floor(score / 10)
  switch (level) {
    case 10:
      sendLevel('等第: A')
      sendMessage('好強!')
      break
    case 9:
    case 8:
      sendLevel('等第: B')
      sendMessage('繼續保持')
      break
    case 7:
      sendLevel('等第: C')
      sendMessage('還有進步空間')
      break
    case 6:
      sendLevel('等第: D')
      sendMessage('要再加強')
      break
    default:
      sendLevel('等第: F')
      sendMessage('加油')
      break
  }
}

利用Object

將判斷條件作為對象的屬性名,將處理邏輯作為對象的屬性值

const getScoreInfo = (score) => {
  let level = score / 10
  const conditionObj = {
    '10': ['等第: A', '好強!'],
    '9': ['等第: B', '繼續保持'],
    '8': ['等第: B', '繼續保持'],
    '7': ['等第: C', '還有進步空間'],
    '6': ['等第: D', '要再加強'],
    'default': ['等第: F', '加油'],
  }
  let action = conditionObj[level] || conditionObj['default']
  let levelInfo = action[0]
  let messageInfo = action[1]

  sendLevel(levelInfo)
  sendMessage(messageInfo)
}

利用ES6 Map對象

跟上一個方法很像,只是Map

附註: 跟Object比較有以下優點

  1. Object的key僅能用字符串或是Symbol對象,但Map可以是任意值
  2. Map對象有size屬性能輕鬆獲得鍵值對個數
const getScoreInfo = (score) => {
  let level = Math.floor(score / 10)
  const conditionMap = new Map([
    [10, ['等第: A', '好強!']],
    [9, ['等第: B', '繼續保持']],
    [8, ['等第: B', '繼續保持']],
    [7, ['等第: C', '還有進步空間']],
    [6, ['等第: D', '要再加強']]
    ['default', ['等第: F', '加油']]
  ])
  let action = conditionMap[level] || conditionMap['default']
  let levelInfo = action[0]
  let messageInfo = action[1]

  sendLevel(levelInfo)
  sendMessage(messageInfo)
}

二元邏輯判斷例子

接下來邏輯判斷變得更複雜,在判斷分數前要先判斷是哪一門科目

if else 寫法

const getScoreInfo = (score, subject) => {
  let level = Math.floor(score / 10)
  if (subject == '國文') {
    if (level == 10) {
      //do sth
    } else if (level == 9) {
      //do sth
    } else if (level == 8) {
      //do sth
    } else if (level == 7) {
      //do sth
    } else if (level == 6) {
      //do sth
    } else {
      //do sth
    }
  } else if (subject == '數學') {
    if (level == 10) {
      //do sth
    } else if (level == 9) {
      //do sth
    } else if (level == 8) {
      //do sth
    } else if (level == 7) {
      //do sth
    } else if (level == 6) {
      //do sth
    } else {
      //do sth
    }
  } 
}

透過字符串拼接條件

簡單來說就是拼接 subject 以及 level

Object

const getScoreInfo = (score, subject) => {
  let level = Math.floor(score / 10)
  const conditionObj = {
    // 拼接條件讓它變成一維
    '國文_10': () => {/*do sth*/ },
    '國文_9': () => {/*do sth*/ },
    '國文_8': () => {/*do sth*/ },
    '國文_7': () => {/*do sth*/ },
    '國文_6': () => {/*do sth*/ },
    '國文_default': () => {/*do sth*/ },
    '數學_10': () => {/*do sth*/ },
    '數學_9': () => {/*do sth*/ },
    '數學_8': () => {/*do sth*/ },
    '數學_7': () => {/*do sth*/ },
    '數學_6': () => {/*do sth*/ },
    '數學_default': () => {/*do sth*/ },
  }
  let action = conditionObj[`${subject}_${level}`] || conditionObj[`${subject}_default`]
  action.call(this)
}

Map對象

其實就是改寫一下conditionObj 變成Map對象

const getScoreInfo = (score, subject) => {
  let level = Math.floor(score / 10)
  const conditionMap = new Map([
    // 拼接條件讓它變成一維
    ['國文_10', () => {/*do sth*/ }],
    ['國文_9', () => {/*do sth*/ }],
    ['國文_8', () => {/*do sth*/ }],
    // ...
  ])
  let action = conditionMap[`${subject}_${level}`] || conditionMap[`${subject}_default`]
  action.call(this)
}

將條件拼接成對象

跟上一題幾乎一樣。

只是把拼接條件變字符串改成對象 (只有Map能這樣,因為Object的key僅用字符串或Symbol)

const getScoreInfo = (score, subject) => {
  let level = Math.floor(score / 10)
  const conditionMap = new Map([
    // 拼接條件讓它變成一維
    [{subject: '國文', level: 10}, () => {/*do sth*/ }],
    [{subject: '國文', level: 9}, () => {console.log('打印')}],
    [{subject: '國文', level: 8}, () => {/*do sth*/ }],
    // ...
  ])
  console.log([...conditionMap]);
  
  let action = [...conditionMap].filter(([key, value] /*解構賦值*/) => {
    return key.subject == subject && key.level == level
  })
  console.log(action);
  action.forEach(([key, value] /*解構賦值*/ ) => value.call(this))
}

getScoreInfo(95, '國文')

https://ithelp.ithome.com.tw/upload/images/20201007/20124350N4UdS82fy6.png

action.forEach(([key, value] /*解構賦值*/ ) => value.call(this))

https://ithelp.ithome.com.tw/upload/images/20201007/20124350rJvj6A28Tf.png

二元延伸例子

假設今天某幾個情況輸出結果都一樣

const conditionMap = new Map([
    // 拼接條件讓它變成一維
    [{subject: '國文', level: 10}, () => { /* functionA */ }],
    [{subject: '國文', level: 9}, () => { /* functionA */ }],
    [{subject: '國文', level: 8}, () => { /* functionA */ }],
    [{subject: '國文', level: 7}, () => { /* functionB */ }],
    // ...
])

將邏輯緩存

const conditionMapFn = () => {
  const functionA = () => {/*do sth*/ }
  const functionB = () => {/*do sth*/ }
  return new Map([
    [{ subject: '國文', level: 10 }, functionA ],
    [{ subject: '國文', level: 9}, functionA ],
    [{ subject: '國文', level: 8}, functionA ],
    [{ subject: '國文', level: 7 }, functionB ],
    //...
  ])
}

const getScoreInfo = (identity, status) => {
  let action = [...conditionMapFn()].filter(([key, value]) => (key.identity == identity && key.status == status))
  action.forEach(([key, value]) => value.call(this))
}

缺點:我們發現返回的Map對象要一直寫functionA,假如今天邏輯判斷有超多且很多都是執行相同函數肯定很麻煩,因此我們利用正則優化他。

利用正則

可以看出Map的key可以設置任何類型真的非常強大

const actions = ()=>{
  const functionA = ()=>{/*do sth*/}
  const functionB = ()=>{/*do sth*/}
  const functionC = ()=>{/*send log*/}
  return new Map([
    [/^guest_[1-4]$/,functionA],
    [/^guest_5$/,functionB],
    [/^guest_.*$/,functionC],
    //...
  ])
}

const onButtonClick = (identity,status)=>{
  let action = [...actions()].filter(([key,value])=>(key.test(`${identity}_${status}`)))
  action.forEach(([key,value])=>value.call(this))
}

總結

作者總結之八種寫邏輯判斷方法 :

https://ithelp.ithome.com.tw/upload/images/20201007/20124350LoZo43vtWN.png


上一篇
Day 21 [其他03] js隱式轉換相關知識
下一篇
Day 23 [編程02] 一些JavaScript中的代碼小技巧
系列文
從技術文章深入學習 JavaScript29
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言